package strategies;

import java.util.Map.Entry;
import java.util.Random;

import stateInfo.Cell;
import stateInfo.CellProperty;
import utilities.Logger;

import com.google.common.base.Function;

/**
 * Concrete strategy based on diffusion
 * 
 * */
public class DiffusionStrategyWithFunctionalWeighting extends AbstractStrategy {
  
  double[][] board;
  Diffuser   diffuser;
  
  double     sensitivityThreshold;
  
  
  public DiffusionStrategyWithFunctionalWeighting() {
    super();
    Logger.log("using " + this.getClass());
    diffuser = new Diffuser(gameState);
 //   myAnts = gameState.getMyAnts( );
    board = new double[Cell.rows][Cell.cols];
    for (int rowCounter = 0; rowCounter < Cell.rows; rowCounter++)
      for (int colCounter = 0; colCounter < Cell.cols; colCounter++)
        board[rowCounter][colCounter] = 0;
    sensitivityThreshold = 0.7;
    
    // TODO Auto-generated constructor stub
  }
  
  
  
  //@Override
  public void doTurn( ) {
    // board = new double[gameState.rows][gameState.cols];
    calcMap( );
//    Logger.log("about to call assignAntOrders");
    
    assignAntOrders( );
    //printBoard();
    Cell currCell = null;
    for (final Entry<Cell, Cell> order: moveManager.ordersFromTo.entrySet( )){
      currCell = order.getKey();
    	moveManager.issueOrder(currCell, order.getValue( ));
      int row = currCell.row;
      int col = currCell.col;
  //    Logger.log("ant at " + row+ " " + col + " value " + board[row][col]);
  /*    for(Cell c: gameState.getLandNeighbors(currCell))
    	  Logger.log(c.row + " " + c.col + " value: " + board[c.row][c.col]);
      Logger.log("assigned ant to move to " + order.getValue().row + " " + order.getValue().col); 
      */
    }
    
  }
  
  
  protected void calcMap( ) {

	    for (Cell c: gameState.getMyHills( ))
	        diffuser.diffuse(board, c, sensitivityThreshold,
	                                                new Function<Cell, Double>( ) {
	                                                  public Double apply(Cell hill) {
	                                                    int range = 10;
	                                                    SimpleBFS search = new SimpleBFS(gameState);
	                                                    if (search.countTargets(range, hill, CellProperty.ENEMY_ANT) > 1) 
	                                                  	  return -15d;
	                                                    else return -50d;
	                                                    
	                                                  }
	                                                });
	      
	     
    for (Cell c: gameState.getEdge())
      diffuser.diffuse(board, c, sensitivityThreshold,
                                              new Function<Cell, Double>( ) {
                                                public Double apply(Cell m) {
                                                  return 400d;
                                                }
                                              });
    
    for (Cell c: gameState.getEnemyHills( ))
        diffuser.diffuse(board, c, sensitivityThreshold,
                                                new Function<Cell, Double>( ) {
                                                  public Double apply(Cell m) {
                                                    return 400d;
                                                  }
                                                });
      
      
      for (Cell c: gameState.getFood( ))
        diffuser.diffuse(board, c, sensitivityThreshold,
                                                new Function<Cell, Double>( ) {
                                                  public Double apply(Cell m) {
                                                	  SimpleBFS search = new SimpleBFS(gameState);
                                                	  if(search.distanceToNearestTarget(15, m, CellProperty.ENEMY_ANT) -
                                                			   search.distanceToNearestTarget(15, m, CellProperty.MY_ANT) > -1)
                                                    return 250d;
                                                	  else return 0d;
                                                  }
                                                });
      

      for (Cell c: gameState.getMyAnts( ))
          diffuser.diffuse(board, c, sensitivityThreshold,
                                                  new Function<Cell, Double>( ) {
                                                    public Double apply(Cell m) {
                                                      
                                                     int antAttackRange = 7;
                                                      int hillAttackRange = 20;
                                                      int hillDefendRange = 12;
                                                      
                                                      double total = 0; 
                                                      
                                                      SimpleBFS search = new SimpleBFS(gameState);
                                                      
                                                      //Logger.log(m+ " distance to nearest food: " + search.distanceToNearestTarget(20, m, CellProperty.FOOD));
                                                      // if there is a threat to the hill and we are nearby, consolidate for defense
                                                   
                                                      if(search.distanceToNearestTarget(25, m, CellProperty.MY_HILL) < 25){
		                                                      for(Cell hill: gameState.getMyHills()){
		                                                        if (search.countTargets(hillDefendRange, hill, CellProperty.ENEMY_ANT) > 1 
		                                                            && search.countTargets(hillDefendRange, m, CellProperty.MY_HILL) > 0)
		                                                          total += 10d;
		                                                        
		                                                      }                                                  
      													}
                                                      // if we are near an enemy hill, consolidate
                                                      if (search.distanceToNearestTarget(hillAttackRange, m, CellProperty.ENEMY_HILL) < 25) 
                                                    	  total += 25d;
                                                     
                                                      
                                                      
                                                      // if there are enemies nearby, consolidate, else disperse                                                  
                                                      int numEnemiesNearby = search.countTargets(antAttackRange,m, CellProperty.ENEMY_ANT);
                                                      //total -= 25 * numEnemiesNearby;
                                                      if(numEnemiesNearby == 0) total -= 200;
                                                      else total += 40;
                                                      
                                                     
                                                      return total;

                                                    }
                                                  });
    
    for (Cell c: gameState.getEnemyAnts( ))
    {
      // Logger.log("enemy ant at " + c.row + " " + c.col);
      diffuser.diffuse(board, c, sensitivityThreshold,
                                              new Function<Cell, Double>( ) {
                                                public Double apply(Cell enemy) {
                                                  double total = 0;
                                                	SimpleBFS search = new SimpleBFS(gameState);
                                                  int attackRange = 12;
                                                  int doOrDieRange = 6;
                                                  int defendRange = 13;
                                                  
                                                  if(search.countTargets(attackRange, enemy, CellProperty.MY_ANT) -1 - 
                                                		  search.countTargets(attackRange, enemy, CellProperty.ENEMY_ANT) > 0)
                                                	  total += 100;

                                                  else total -= 100;
                                                  
                                                  if(search.countTargets(defendRange, enemy, CellProperty.MY_HILL)>0) total += 75;

                                                  if(search.countTargets(doOrDieRange, enemy, CellProperty.MY_HILL) > 0)
                                                	  total += 100;
                                                  return total;
                                                  
                                                }
                                              });
    }
    
    
  }
  
  
  private void printBoard( ) {
    for (int rowCounter = 0; rowCounter < board.length; rowCounter++) {
      for (int colCounter = 0; colCounter < board[0].length; colCounter++)
        if (board[rowCounter][colCounter] != 0) Logger.log(rowCounter + " " + colCounter + " : " + board[rowCounter][colCounter] + "   ");
      
    }
  }
  
  
  protected void assignAntOrders( ) {    
    Cell bestCell = null;
    int currRow = 0, currCol = 0;
    double bestVal;
    for (Cell a: myAnts) {
      bestCell = a;
      bestVal = board[a.row][a.col];
      for (Cell c: gameState.getLandNeighbors(a)) {
        currRow = c.row;
        currCol = c.col;
        double currVal = board[currRow][currCol];
        Logger.log("value of cell at " + currRow + " " + currCol + " is " + currVal);
        if (currVal >= bestVal) {
          bestVal = currVal;
          bestCell = c;
          
        }
      }
      if (gameState.isATrapCell(bestCell)) {
    	  Random r = new Random();
    	  int randChoice = r.nextInt(gameState.getLandNeighbors(a, true).size());
    	  bestCell = gameState.getLandNeighbors(a, true).asList().get(randChoice);
      }
      bestCell = getAwayFromHill(a, bestCell);
     
      if (!(moveManager.ordersFromTo.containsValue(bestCell))) moveManager.ordersFromTo.put(a, bestCell);
      //Logger.log("assigned ant at cell " + a.row + " " + a.col + "  to move to " + bestCell.row + " " + bestCell.col);
    }
    
    // prevent freezing due to call for help weighting cell.  Find a more elegant way to do this!
    for(Cell c: gameState.getMyAnts()) board[c.row][c.col] = 0;
  }
  
  
  protected Cell getAwayFromHill(Cell ant, Cell bestCell) {
    for (Cell hill: gameState.getMyHills( )) {
      if (!(hill.equals(ant)) )
          if(gameState.getLandNeighbors(hill).contains(bestCell)) 
            for (Cell c: gameState.getLandNeighbors(ant))
              if(!(hill.equals(c))&& !(gameState.getLandNeighbors(hill).contains(c))) bestCell = c;
      if(hill.equals(bestCell)) bestCell =gameState.getLandNeighbors(ant).asList().get(0);
  }
    return bestCell;
  }
  
  
}
